﻿using System;
using System.Threading;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

using GPSCrowdSource.Algorithms;
using GPSCrowdSource.DataStructures;

namespace SimpleDemo {

  /// <summary>
  /// Simple analysis demo application.
  /// </summary>
  public partial class SimpleAnalysisDemo : Form {

    #region private
    
    private ChartArea myArea;
    private Series myRouteSeries;
    private Series myGPSSeries;
    private Series myStationSeries;

    private GPSListDataSource myGPS = new GPSListDataSource();
    #endregion

    private void Attach(bool inV) {
      Action a = delegate() {
        myChart.MouseClick -= myChart_MouseClick;

        if(inV)  {
          myChart.MouseClick += myChart_MouseClick;
          }

        myAnalyze.Enabled=inV;
        myClearGPS.Enabled=inV;

        };

      if(InvokeRequired)
        Invoke(a);
      else
        a();
      } 

    /// <summary>
    /// Set the station color.
    /// </summary>
    private void UpdateStation(Station station, Color color, Departure inDep) {

      Action a = delegate() {
        for(int i=0;i<myStationSeries.Points.Count;i++) {
          Station s = (Station)myStationSeries.Points[i].Tag;
          if(s.Name == station.Name) {
            myStationSeries.Points[i].Color = color;
            String lbl = inDep.Time.ToString() + " " + inDep.Direction;
            myStationSeries.Points[i].Label = lbl;
            break;
            }
          } 
        };

      if(InvokeRequired)
        BeginInvoke(a);
      else
        a();

      }
    
    /// <summary>
    /// Set the status text.
    /// </summary>
    /// <param name="inMsg"></param>
    private void Status(String inMsg) {
      
      Action a = delegate() {
        myStatus.Text = inMsg;  
        };

      if(InvokeRequired)
        BeginInvoke(a);
      else
        a();
      
      }

    /// <summary>
    /// Clear GPS and colors and stuff
    /// </summary>
    private void Reset() {

      Action a = delegate() {

        myGPS.Clear();
        myGPSSeries.Points.Clear();

        for(int i=0;i<myStationSeries.Points.Count;i++) {
          Station s = (Station)myStationSeries.Points[i].Tag;
          myStationSeries.Points[i].Color = Color.Black;
          myStationSeries.Points[i].Label = s.Name;
          }
        };

      if(InvokeRequired)
        BeginInvoke(a);
      else
        a();
      }

    public SimpleAnalysisDemo() {
      InitializeComponent();
      Status("Click on the map to add GPS points");

      myArea = myChart.ChartAreas.Add("Rail D");
      
      myRouteSeries = myChart.Series.Add("Route");
      myRouteSeries.Name = "Route";
      myRouteSeries.ChartArea = "Rail D";
      myRouteSeries.ChartType = SeriesChartType.Spline;
      myRouteSeries.IsValueShownAsLabel =false;

      myGPSSeries = myChart.Series.Add("GPS");
      myGPSSeries.ChartArea = "Rail D";
      myGPSSeries.ChartType = SeriesChartType.Point;

      myStationSeries = myChart.Series.Add("Stations");
      myStationSeries.ChartArea = "Rail D";
      myStationSeries.ChartType = SeriesChartType.Point;
      
      Attach(true);

			Route r = RTDDatabase.Instance.Query(eRTDLine.D);

      double minX = double.MaxValue;
      double maxX = double.MinValue;
      double minY = double.MaxValue;
      double maxY = double.MinValue;

      foreach(Station s in r) {
        if(s.Latitude > maxY)
          maxY = s.Latitude;
        if(s.Latitude < minY)
          minY = s.Latitude;
        if(s.Longitude > maxX)
          maxX = s.Longitude;
        if(s.Longitude < minX)
          minX = s.Longitude;
        }

      myArea.AxisX.MajorGrid.Enabled=false;
      myArea.AxisY.MajorGrid.Enabled=false;
      myArea.AxisX.LabelStyle.Enabled=false;
      myArea.AxisY.LabelStyle.Enabled=false;
            
      myArea.AxisX.Minimum = minX;
      myArea.AxisX.Maximum = maxX;
      myArea.AxisY.Minimum = minY;
      myArea.AxisY.Maximum = maxY;

      double[] x = new double[r.Count];
      double[] y = new double[r.Count];

      for(int i=0;i<r.Count;i++) {
        Station s = r[i];
        x[i] = s.Longitude;
        y[i] = s.Latitude;
        }

      myRouteSeries.Points.DataBindXY(x,y);
      
      for(int i=0;i<r.Count;i++) {
        Station s = r[i];
        DataPoint dp = new DataPoint(s.Longitude,s.Latitude);
        dp.Color = Color.Black;
        dp.MarkerSize = 10;
        dp.MarkerStyle = MarkerStyle.Triangle;
        dp.Label = r[i].Name;
        dp.Tag = r[i];
        myStationSeries.Points.Add(dp);
        }

      }

    private void myChart_MouseClick(object sender, MouseEventArgs e) {
      double x = myArea.AxisX.PixelPositionToValue(e.X);
      double y = myArea.AxisY.PixelPositionToValue(e.Y);
      myGPS.Add(new GeoPosition((float)y,(float)x));

      List<GeoPosition> gpp = new List<GeoPosition>(myGPS);
      myGPSSeries.Points.Clear();
      for(int i=0;i<myGPS.Count;i++) {
        DataPoint dp = new DataPoint(gpp[i].Longitude,gpp[i].Latitude);
        dp.Color = Color.Blue;
        myGPSSeries.Points.Add(dp);
        }

      }

    private void mySimulate_Click(object sender, EventArgs e) {
      
      Thread th = new Thread(delegate() {
        Attach(false);

        AnalysisResults ans;
        RouteAnalyzer ra = new RouteAnalyzer();

        try {
          
          while(true) {
            ans = ra.AnalyzeFully(myGPS);

            if(ans.Answer == eAnalysisAnswer.NoGpsData) {
              Status("Not enough GPS data");
              break;
              }

            switch(ans.Answer) {
              case eAnalysisAnswer.NoRouteData:
                Status("Not enough route data");
                break;
              case eAnalysisAnswer.Late:
                Status("Rail is not on time!");
                UpdateStation(ans.Station,Color.Red,ans.Departure);
                break;
              case eAnalysisAnswer.OnTime:
                Status("Rail is On Time!");
                UpdateStation(ans.Station,Color.Green,ans.Departure);
                break;
              case eAnalysisAnswer.Unknown:
                Status("Unknown error, see log!");
                break;
              }

            Thread.Sleep(2000);
            }

          }
        catch(ThreadInterruptedException){}
        catch(ThreadAbortException) {}
        catch(Exception ex) {
          Core.Util.Log.Error(ex);
          }

        Reset();
        Thread.Sleep(1000);
        Status("Click on the map to add GPS points");
        Attach(true);
        });

      th.IsBackground=true;
      th.Name="GPS";
      th.Start();
      }

    private void myClearGPS_Click(object sender, EventArgs e) {
      Reset();
      }

    private void myClose_Click(object sender, EventArgs e) {
      Dispose();
      }

    }

  }
